import re
import json
import multiprocessing
from pathlib import Path

from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString
from docx import Document
from pygments import highlight
from pygments.formatters.html import HtmlFormatter
from pygments.lexers import guess_lexer_for_filename

from common import util
from common.report import ReportDocument

"""
生成实验报告需要的函数，用于实现核心流程

function:
    preprocess: 预处理代码，处理后生成中间文件
    generate_semi_html: 将代码转换成html格式的高亮文件
    generate_semi_json: 处理html文件，生成json文件便于使用
    generate_report: 根据上一步的json文件生成实验报告
"""


def preprocess(config: dict):
    """
    预处理文件，将代码最终转换成json文件

    :param config: 生成实验报告需要的配置项
        title: 标题
        meta: 作者信息
        dest: 生成实验报告的目标路径
    """

    # 清空文件夹，防止外来的文件污染处理过程
    dest_path = Path(config["dest"])

    semi_path = dest_path / "semi"
    if not semi_path.exists():
        semi_path.mkdir()
    util.empty_directory(semi_path)

    # 生成html文件
    html_pool = multiprocessing.Pool(processes=10)
    code_path = dest_path / "code"
    for file in code_path.iterdir():
        if not file.is_file():
            continue
        html_pool.apply_async(generate_semi_html, (semi_path, file))
    html_pool.close()
    html_pool.join()

    # 根据上一步生成的html文件生成json文件，便于后面进行处理
    json_pool = multiprocessing.Pool(processes=10)
    for file in semi_path.iterdir():
        if not file.is_file():
            continue
        if "html" in file.suffix:
            json_pool.apply_async(generate_semi_json, (semi_path, file))
    json_pool.close()
    json_pool.join()


def generate_semi_html(semi_path: Path, file: Path):
    """
    根据源代码文件生成带有高亮的html格式中间文件，使用Pygments库实现

    :param semi_path: 需要存放文件的路径
    :param file: 需要解析的源代码路径
    """
    code = file.read_text(encoding="utf-8")
    content = highlight(code=code,
                        lexer=guess_lexer_for_filename(file.name, code),
                        formatter=HtmlFormatter(font_name="FangSong",
                                                style="default",
                                                noclasses=True))

    semi_file = semi_path / (str(file.name) + ".html")
    semi_file.write_text(content, encoding="utf-8")


def generate_semi_json(semi_path: Path, html_file: Path):
    """
    解析html格式的中间文件，去掉无用的信息，生成json格式的中间文件，使用BS4库实现

    :param semi_path: 需要存放文件的路径
    :param html_file: 需要解析的html格式文件路径
    """
    html_content = html_file.read_text(encoding="utf-8")
    soup = BeautifulSoup(html_content, "html.parser")
    contents = []

    for child in soup.pre.children:
        # 略过没有字符串的内容
        if child.string is None:
            continue

        # 解析出文字的颜色
        if isinstance(child, Tag):
            color = re.search("#[0-9a-fA-F]{6}", child.attrs["style"]).group().lower()
            bold: bool = True if "bold" in child.attrs["style"] else False
            contents.append({"content": child.string, "color": color, "bold": bold})

        if isinstance(child, NavigableString):
            contents.append({"content": child.string, "color": "#000000", "bold": False})

    semi_json_path = semi_path / (html_file.stem + ".json")
    with semi_json_path.open(mode="w", encoding="utf-8") as fp:
        json.dump({"contents": contents}, fp, indent=2)


def generate_report(config: dict):
    """
    根据之前的中间文件生成实验报告

    :param config: 实验报告的配置信息，参数同generate函数
    """
    # 获取实验报告生成路径
    dest_path = Path(config["dest"])
    report_path: str = str((dest_path / "report.docx").absolute())
    semi_path = dest_path / "semi"

    # 创建文件对象，添加标题，作者等信息
    document = ReportDocument(Document())
    document.add_title(config["title"])
    document.add_meta(config["meta"])

    # 添加警告信息，这里可以考虑放在页眉
    document.add_header("注意：这里文件按照字母顺序进行排列，这意味着1后面是10而不是2")
    document.add_page_break()

    # 遍历semi文件夹，将代码写入到文档中
    for semi_file in semi_path.iterdir():
        if "json" not in semi_file.suffix:
            continue

        # 写入文件名
        filename_without_suffix = semi_file.stem
        document.add_paragraph(filename_without_suffix + ": ", new_paragraph=True)
        document.set_font(16, bold=True)

        # 写入代码，每写完一个文件就添加一个分页符
        contents: list
        with semi_file.open(mode="r", encoding="utf-8") as fp:
            document.add_paragraph("", new_paragraph=True)
            contents = json.load(fp)["contents"]
        for content in contents:
            text = content["content"]
            r, g, b = util.hex2rgb(content["color"])
            document.add_paragraph(text)
            document.set_color(r, g, b)
            document.set_font(14)
        # 添加分页符
        document.add_page_break()

    # 保存文件
    document.save(report_path)
